﻿//TODO: SegmentationOtus, Check if result is ok

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class SegmentationOtus : BaseImageFilter
    {
        protected int m_nThresholdValue = 1;

        /// <summary>
        /// Default constructor
        /// </summary>
        public SegmentationOtus()
        {
        }

        /// <summary>
        /// Use this to create a cloned instance
        /// </summary>
        internal SegmentationOtus(SegmentationOtus _segmentationOtus)
        {
        }

        /// <summary>
        /// Gets or sets threshold value
        /// </summary>
        public int ThresholdValue
        {
            get { return m_nThresholdValue;  }
            set { m_nThresholdValue = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap m_Bitmap = (UnsafeBitmap)_bitmap.Clone();
            UnsafeBitmap bm = (UnsafeBitmap)_bitmap.Clone();
            UnsafeBitmap ebm = (UnsafeBitmap)_bitmap.Clone();

            int[] aHistogram = new int[256];

            int i = 0, j = 0, k = 0; // various counters
            int n = 0, n1 = 0, n2 = 0, gmin = 0, gmax = 0;
            double m1 = 0, m2 = 0, sum = 0, csum = 0, fmax = 0, sb = 0;

            //
            int nGrayLevel = 255;
            double[] Pk = new double[256];
            double[] sk = new double[256];
            int[] Sk = new int[256];
            int nImgN = 0;
            double dTempSum = 0.0f;

            nImgN = bm.Width * bm.Height;

            for (i = 0; i < 255; i++)
            {
                aHistogram[i] = 0;
            }

            for (int y = 0; y < bm.Height; y++)
            {
                for (int x = 0; x < bm.Width; x++)
                {
                    Color c = m_Bitmap.GetPixel(x, y);

                    int luma = (int)(c.R * 0.3f + c.G * 0.59f + c.B * 0.11f);

                    bm.SetPixel(x, y, Color.FromArgb(luma, luma, luma));
                    aHistogram[luma]++;
                }
            }

            UnsafeBitmap bitmap = _bitmap;
            for (i = 0; i < 256; i++)
            {
                Pk[i] = Convert.ToDouble(aHistogram[i]) / Convert.ToDouble(nImgN);
                if (i == 0)
                {
                    sk[i] = Pk[i];
                }
                else
                {
                    sk[i] = sk[i - 1] + Pk[i];
                }
                Sk[i] = Convert.ToInt16((nGrayLevel * sk[i])) + 1;
            }

            for (int y = 0; y < bm.Height; y++)
            {
                for (int x = 0; x < bm.Width; x++)
                {
                    Color c = m_Bitmap.GetPixel(x, y);

                    int nLuminance = Sk[c.R];

                    if (nLuminance > 255)
                        nLuminance = 255;

                    ebm.SetPixel(x, y, Color.FromArgb(nLuminance, nLuminance, nLuminance));
                }
            }

            gmin = 255;
            gmax = 0;

            // set up everything
            sum = csum = 0.0;
            n = 0;
            for (k = 0; k <= 255; k++)
            {
                sum += (double)k * (double)aHistogram[k]; /* x*f(x) ÖÊÁ¿¾Ø*/
                n += aHistogram[k]; /* f(x) ÖÊÁ¿ */
            }
            if (n == 0)
            {
                // if n has no value, there is problems...
                bitmap.Dispose();
                return _bitmap;
            }
            // do the otsu global thresholding method
            fmax = -1.0;
            n1 = 0;
            for (k = 0; k < 255; k++)
            {
                n1 += aHistogram[k];
                if (n1 == 0)
                {
                    continue;
                }
                n2 = n - n1;
                if (n2 == 0)
                {
                    break;
                }
                csum += (double)k * aHistogram[k];
                m1 = csum / n1;
                m2 = (sum - csum) / n2;
                sb = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2);
                /* bbg: note: can be optimized. */
                if (sb > fmax)
                {
                    fmax = sb;
                    m_nThresholdValue = k;
                }
            }
            // at this point we have our thresholding value
            //MessageBox.Show(thresholdValue.ToString());

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    Color color = bitmap.GetPixel(x, y);

                    int nLuminance = (int)(color.R);

                    if (nLuminance >= m_nThresholdValue)
                    {
                        bitmap.SetPixel(x, y, Definitions.White);
                    }
                    else
                    {
                        bitmap.SetPixel(x, y, Definitions.Black);
                    }
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Use this to clone the filter
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new SegmentationOtus(this);
        }
    }
}
